package cc.rengu.igas.channel.wechat.core.packswitch;

import cc.rengu.igas.channel.wechat.common.constant.WechatTreeNodeConstant;
import cc.rengu.igas.channel.wechat.core.realize.WechatSignService;
import cc.rengu.igas.channel.wechat.facade.base.BaseRequest;
import cc.rengu.oltp.service.common.constant.AppParamConstant;
import cc.rengu.oltp.service.common.constant.TreeNodeConstant;
import cc.rengu.oltp.service.common.dao.*;
import cc.rengu.oltp.service.common.dao.impl.*;
import cc.rengu.oltp.service.common.entity.*;
import cc.rengu.igas.channel.wechat.common.constant.WechatParamConstant;
import cc.rengu.igas.channel.wechat.common.enums.RespCodeEnum;
import cc.rengu.igas.channel.wechat.core.realize.impl.WechatSignServiceImpl;
import cc.rengu.jradp.packetswitch.IncomingPackSwitchImpl;
import cc.rengu.jradp.packetswitch.OutgoingPackSwitchImpl;
import cc.rengu.oltp.service.model.BizException;
import cc.rengu.oltp.utility.util.*;
import cc.rengu.utility.base.StringUtil;
import cc.rengu.utility.log.RgLog;
import cc.rengu.utility.log.RgLogger;
import cc.rengu.utility.xml.XmlDocument;
import cc.rengu.utility.xml.XmlError;
import cc.rengu.utility.xml.XmlValue;

import java.io.ByteArrayOutputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Optional;

/**
 * 与微信通讯层报文解包前后、组前包后实现类
 * Created by 王成 on 2018/5/31.
 */
public class WechatClientPackSwitch implements IncomingPackSwitchImpl, OutgoingPackSwitchImpl {

    @Override
    public byte[] beforeInComPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String signType = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGN_TYPE);
        /* 获取数字签名字符串 */
        String xmlBefore = new String(bytes);
        /*更新业务报文信息*/
        updateBizMsgInfo(rglog, xmlBefore);
        XmlDocument xmlDocument = new XmlDocument();
        XmlError xmlError = xmlDocument.loadString(xmlBefore);
        if (!xmlError.isSuccess()) {
            rglog.error("接收微信报文异常!");
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
        } else {
            XmlValue xmlvalue = xmlDocument.getValue(WechatTreeNodeConstant.WX_SIGN, xmlError);
            if (null == xmlvalue || StringUtil.isNullorEmpty(xmlvalue.getText())) {
                rglog.debug("无验签域，不进行签名校验!");
                return bytes;
            }
            String signData = xmlvalue.getText();
            /* 生成生成数字签名的字符串 */
            WechatSignService wechatSignService = new WechatSignServiceImpl();
            String signBlockString = wechatSignService.generateSignBlockString(xmlBefore);
            if (null == signBlockString) {
                rglog.error("生成加签Block失败!");
                throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
            }
            rglog.debug("加签Block:<{}>", signBlockString);
            /* 获取微信验签密钥信息 */
            String signKey = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGN_KEY);
            boolean verifyFlag = false;
            if (WechatParamConstant.HMAC_SHA256_SIGNTYPE.equals(signType)) {
                verifyFlag = HmacUtil.verifyHmacSHA256SignString(signData, signBlockString, signKey);
            } else {
                verifyFlag = wechatSignService.verifyMD5SignString(signData, signBlockString, signKey);
            }
            if (!verifyFlag) {
                rglog.error("验签失败!");
                throw new BizException(RespCodeEnum.VERIFY_SIGN_ERROR.getRespCode(), RespCodeEnum.VERIFY_SIGN_ERROR.getRespDesc());
            }
        }
        return bytes;
    }

    @Override
    public int afterInComPackSwitch() throws Exception {
        return 0;
    }

    @Override
    public int beforeOutgoPackSwitch() throws Exception {
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String nonceStr = RandomUtil.randomString(WechatTreeNodeConstant.WX_RAND_STR_LEN);
        xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_RAND_STR, nonceStr);
        /* total_fee转换成分为单位的金额 */
        String orderAmt = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.ORDER_AMT);
        xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.ORDER_AMT, AmountUtil.changeY2F(orderAmt));
        String refundAmt = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.REFUND_AMT);
        if (!StringUtil.isNullorEmpty(refundAmt)) {
            xmlTreeUtil.setXmlTreeStringValue(TreeNodeConstant.REFUND_AMT, AmountUtil.changeY2F(refundAmt));
        }
        return 0;
    }

    @Override
    public byte[] afterOutgoPackSwitch(byte[] bytes) throws Exception {
        RgLogger rglog = RgLog.getLogger(this.getClass().getName());
        XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
        String instId = xmlTreeUtil.getXmlTreeStringValue(TreeNodeConstant.INST_ID);
        String signType = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGN_TYPE);
        String msgRegisterFlag = xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.WECHAT_MSG_REG_FLAG);
        String signKey = null;
        String signData = null;
        DstChannelCfg dstChannelCfg = (DstChannelCfg) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.DST_CHANNEL_CFG, DstChannelCfg.class);
        /* 根据通道安全计划获取安全计划 */
        SecPlanInfoMapper secPlanInfoMapper = new SecPlanInfoMapperImpl();
        List<SecPlanInfo> secPlanInfoList = secPlanInfoMapper.selectSecPlanInfoBySecPlanId(instId, dstChannelCfg.getSecPlanId());
        if (null != secPlanInfoList && !secPlanInfoList.isEmpty()) {
            Optional<SecPlanInfo> secPlanInfoOpt = secPlanInfoList.stream().filter(item -> item.getSecPlanType().equals(WechatParamConstant.SIGN_KEY_TYPE)).findFirst();
            if (secPlanInfoOpt.isPresent()) {
                KeyInfoMapper keyInfoMapper = new KeyInfoMapperImpl();
                KeyInfo keyInfo = keyInfoMapper.selectKeyInfoByPrimaryKey(secPlanInfoOpt.get().getInstId() + secPlanInfoOpt.get().getSecPlanId() + secPlanInfoOpt.get().getSecPlanType());
                if (keyInfo != null) {
                    signKey = keyInfo.getKeyValue();
                    xmlTreeUtil.setXmlTreeStringValue(WechatTreeNodeConstant.WX_SIGN_KEY, signKey);
                }
            }
        }
        if (signKey == null) {
            rglog.error("获取服务商签名密钥失败");
            throw new BizException(RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespCode(), RespCodeEnum.GET_THIRD_MCHNT_KEY_ERROR.getRespDesc());
        }
        rglog.debug("key=[{}]", signKey);
        String xmlBefore = new String(bytes);
        /* 生成生成数字签名的字符串 */
        WechatSignService wechatSignService = new WechatSignServiceImpl();
        String signBlockString = wechatSignService.generateSignBlockString(xmlBefore);
        if (null == signBlockString) {
            rglog.error("生成微信加签Block失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_BLOCK_ERROR.getRespDesc());
        }
        rglog.debug("系统签名算法:<{}>,加签Block:<{}>", signType, signBlockString);
        if (WechatParamConstant.HMAC_SHA256_SIGNTYPE.equals(signType)) {
            signData = HmacUtil.generateHmacSHA256SignString(signBlockString + "&key=" + signKey, signKey);
        } else {
            signData = wechatSignService.generateMD5SignString(signBlockString, signKey);
        }
        if (null == signData) {
            rglog.error("生成数字签名失败!");
            throw new BizException(RespCodeEnum.GENERATE_SIGN_ERROR.getRespCode(), RespCodeEnum.GENERATE_SIGN_ERROR.getRespDesc());
        }
        rglog.debug("数字签名:<{}>", signData);
        /* 将数字签名设置到报文中 */
        XmlDocument xmlDocument = new XmlDocument();
        XmlError xmlError = xmlDocument.loadString(xmlBefore);
        if (!xmlError.isSuccess()) {
            rglog.error("解析微信报文异常!");
            throw new BizException(RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespCode(), RespCodeEnum.MSG_VALID_CHECK_ERROR.getRespDesc());
        } else {
            xmlDocument.setValue(WechatTreeNodeConstant.WX_SIGN, new XmlValue(signData));
            /*登记业务报文表*/
            if (AppParamConstant.YES.equals(msgRegisterFlag)) {
                registerBizMsgInfo(rglog, xmlDocument.toString());
            }
            return xmlDocument.toString().getBytes();
        }
    }

    private void registerBizMsgInfo(RgLogger rglog, String bizMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizMsg);
                return;
            }
            BizMsgInfo bizMsgInfo = new BizMsgInfo();
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            BaseRequest baseRequest = new BaseRequest();
            ConvertUtil.convertOutput(baseRequest);
            bizMsgInfo.setInstId(baseRequest.getHeader().getInstId());
            bizMsgInfo.setChannelId(xmlTreeUtil.getXmlTreeStringValue(WechatTreeNodeConstant.CHANNEL_ID));
            bizMsgInfo.setTransSeqNum(baseRequest.getHeader().getTraceNo());
            bizMsgInfo.setTxnDate(baseRequest.getHeader().getTransDate());
            bizMsgInfo.setTxnTime(baseRequest.getHeader().getTransTime());
            bizMsgInfo.setDstTxnNum(baseRequest.getHeader().getTxnNum());
            bizMsgInfo.setCreateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizMsg.length() < 4000) {
                bizMsgInfo.setBizReqMsg(bizMsg);
            } else {
                bizMsgInfo.setBizReqMsg(bizMsg.substring(0, 4000));
                bizMsgInfo.setBizReqExpandMsg(bizMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.insertBizMsgInfo(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
            xmlTreeUtil.setXmlTreeObjectValue(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO, bizMsgInfo);
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }

    private void updateBizMsgInfo(RgLogger rglog, String bizRspMsg) {
        try {
            XmlTreeUtil xmlTreeUtil = new XmlTreeUtil();
            if (bizRspMsg.length() > 8000) {
                rglog.error("存储转发报文超过8K，暂不登记到存储转发表:<{}>", bizRspMsg);
                return;
            }
            if (!xmlTreeUtil.isExist(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO)) {
                rglog.error("获取请求业务报文节点失败，不更新");
                return;
            }
            BizMsgInfo bizMsgInfo = (BizMsgInfo) xmlTreeUtil.getXmlTreeObjectValue(WechatTreeNodeConstant.WECHAT_BIZ_MSG_INFO, BizMsgInfo.class);
            BizMsgInfoMapper bizMsgInfoMapper = new BizMsgInfoMapperImpl();
            bizMsgInfo.setUpdateTime(DateUtil.getCurrentDateTime("yyyy-MM-dd HH:mm:ss.SSS"));
            if (bizRspMsg.length() < 4000) {
                bizMsgInfo.setBizRspMsg(bizRspMsg);
            } else {
                bizMsgInfo.setBizRspMsg(bizRspMsg.substring(0, 4000));
                bizMsgInfo.setBizRspExpandMsg(bizRspMsg.substring(4000));
            }
            int iReturnCode = bizMsgInfoMapper.updateBizMsgInfoByTransSeqNum(bizMsgInfo);
            if (0 != iReturnCode) {
                return;
            }
        } catch (Exception e) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            e.printStackTrace(new PrintWriter(byteArrayOutputStream, true));
            rglog.error("登记业务报文信息表异常:<{}>", byteArrayOutputStream.toString());
        }
    }

}
